﻿using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;

namespace GBricks
{
  partial class Lambda
  {
    private delegate Func<T, R> RecursiveFunc<T, R>(RecursiveFunc<T, R> r);
    private delegate Func<T1, T2, TResult> RecursiveFunc<T1, T2, TResult>(RecursiveFunc<T1, T2, TResult> r);
    private delegate Func<T1, T2, T3, TResult> RecursiveFunc<T1, T2, T3, TResult>(RecursiveFunc<T1, T2, T3, TResult> r);
    private delegate Action RecursiveAction(RecursiveAction r);
    private delegate Action<T> RecursiveAction<T>(RecursiveAction<T> r);
    private delegate Action<T1, T2> RecursiveAction<T1, T2>(RecursiveAction<T1, T2> r);
    private delegate Action<T1, T2, T3> RecursiveAction<T1, T2, T3>(RecursiveAction<T1, T2, T3> r);

    [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Y", Justification = Justifications.StandardIdentifier)]
    [DebuggerHidden, DebuggerStepThrough]
    public static Func<T, TResult> Y<T, TResult>(Func<Func<T, TResult>, Func<T, TResult>> function) {
      Argument.NotNull(function, "function");
      Contract.Ensures(Contract.Result<Func<T, TResult>>() != null);

      RecursiveFunc<T, TResult> rec = r => a => function(r(r))(a);
      return rec(rec);
    }

    [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Y", Justification = Justifications.StandardIdentifier)]
    [DebuggerHidden, DebuggerStepThrough]
    public static Func<T1, T2, TResult> Y<T1, T2, TResult>(Func<Func<T1, T2, TResult>, Func<T1, T2, TResult>> function) {
      Argument.NotNull(function, "function");
      Contract.Ensures(Contract.Result<Func<T1, T2, TResult>>() != null);

      RecursiveFunc<T1, T2, TResult> rec = r => (a1, a2) => function(r(r))(a1, a2);
      return rec(rec);
    }

    [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Y", Justification = Justifications.StandardIdentifier)]
    [DebuggerHidden, DebuggerStepThrough]
    public static Func<T1, T2, T3, TResult> Y<T1, T2, T3, TResult>(Func<Func<T1, T2, T3, TResult>, Func<T1, T2, T3, TResult>> function) {
      Argument.NotNull(function, "function");
      Contract.Ensures(Contract.Result<Func<T1, T2, T3, TResult>>() != null);

      RecursiveFunc<T1, T2, T3, TResult> rec = r => (a1, a2, a3) => function(r(r))(a1, a2, a3);
      return rec(rec);
    }

    [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Y", Justification = Justifications.StandardIdentifier)]
    [DebuggerHidden, DebuggerStepThrough]
    public static Action Y(Func<Action, Action> function) {
      Argument.NotNull(function, "function");
      Contract.Ensures(Contract.Result<Action>() != null);

      RecursiveAction recursion = r => () => function(r(r))();
      return recursion(recursion);
    }

    [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Y", Justification = Justifications.StandardIdentifier)]
    [DebuggerHidden, DebuggerStepThrough]
    public static Action<T> Y<T>(Func<Action<T>, Action<T>> function) {
      Argument.NotNull(function, "function");
      Contract.Ensures(Contract.Result<Action<T>>() != null);

      RecursiveAction<T> recursion = r => x => function(r(r))(x);
      return recursion(recursion);
    }

    [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Y", Justification = Justifications.StandardIdentifier)]
    [DebuggerHidden, DebuggerStepThrough]
    public static Action<T1, T2> Y<T1, T2>(Func<Action<T1, T2>, Action<T1, T2>> function) {
      Argument.NotNull(function, "function");
      Contract.Ensures(Contract.Result<Action<T1, T2>>() != null);

      RecursiveAction<T1, T2> recursion = r => (x, y) => function(r(r))(x, y);
      return recursion(recursion);
    }

    [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Y", Justification = Justifications.StandardIdentifier)]
    [DebuggerHidden, DebuggerStepThrough]
    public static Action<T1, T2, T3> Y<T1, T2, T3>(Func<Action<T1, T2, T3>, Action<T1, T2, T3>> function) {
      Argument.NotNull(function, "function");
      Contract.Ensures(Contract.Result<Action<T1, T2, T3>>() != null);

      RecursiveAction<T1, T2, T3> recursion = r => (a1, a2, a3) => function(r(r))(a1, a2, a3);
      return recursion(recursion);
    }
  }
}
